package ren.nearby.lib.ui.statusbar;

import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 刘海屏适配
 * Created by wuchundu on 2018/9/17.
 */
public class NotchCompat {

    static {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            IMPL = new DefaultNotchScreen();
        }
//        else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
//            IMPL = new PNotchScreenSupport();
//        }
        else if (RomUtils.isHuaweiRom()) {
            IMPL = new HwNotchScreenSupport();
        } else if (RomUtils.isMiuiRom()) {
            IMPL = new MiNotchScreenSupport();
        } else if (RomUtils.isOppoRom()) {
            IMPL = new OppoNotchScreenSupport();
        } else if (RomUtils.isVivoRom()) {
            IMPL = new VivoNotchScreenSupport();
        } else {
            IMPL = new DefaultNotchScreen();
        }
    }

    private static INotchScreen IMPL;

    /**
     * 判断屏幕是否为刘海屏
     * WindowInsets在View Attach到Window上之后才会创建
     * 因此想要获得正确的结果，方法的调用时机应在DecorView Attach之后
     */
    public static boolean hasDisplayCutout(Window window) {
        return IMPL.hasNotchInScreen(window);
    }

    /**
     * 获取刘海屏大小
     */
    public static List<Rect> getDisplayCutoutSize(Window window) {
        return IMPL.getNotchSize(window);
    }

    /**
     * 设置始终使用刘海屏区域
     */
    public static void immersiveDisplayCutout(Window window) {
        IMPL.setWindowLayoutAroundNotch(window);
    }

    /**
     * 设置始终不使用刘海屏区域
     */
    public static void blockDisplayCutout(Window window) {
        IMPL.setWindowLayoutBlockNotch(window);
    }


    interface INotchScreen {

        boolean hasNotchInScreen(Window window);

        List<Rect> getNotchSize(Window window);

        void setWindowLayoutAroundNotch(Window window);

        void setWindowLayoutBlockNotch(Window window);
    }


    /**
     * Android O 以下的刘海屏适配
     */
    private static class DefaultNotchScreen implements INotchScreen {

        @Override
        public boolean hasNotchInScreen(Window window) {
            return false;
        }

        @Override
        public List<Rect> getNotchSize(Window window) {
            ArrayList<Rect> result = new ArrayList<>();
            Context context = window.getContext();
            Rect rect = new Rect();
            rect.top = 0;
            rect.left = 0;
            rect.bottom = getStatusBarHeight(context);
            rect.right = context.getResources().getDisplayMetrics().widthPixels;
            result.add(rect);
            return result;
        }

        @Override
        public void setWindowLayoutAroundNotch(Window window) {
        }

        @Override
        public void setWindowLayoutBlockNotch(Window window) {
        }
    }

    /**
     * Android P 以上的刘海屏适配
     */
//    private static class PNotchScreenSupport implements INotchScreen {
//
//        @RequiresApi(api = Build.VERSION_CODES.P)
//        @Override
//        public boolean hasNotchInScreen(Window window) {
//            View decorView = window.getDecorView();
//            WindowInsets windowInsets = decorView.getRootWindowInsets();
//            if (windowInsets == null){
//                return false;
//            }
//            DisplayCutout dct = windowInsets.getDisplayCutout();
//            return dct != null && (dct.getSafeInsetTop() != 0
//                    || dct.getSafeInsetBottom() != 0
//                    || dct.getSafeInsetLeft() != 0
//                    || dct.getSafeInsetRight() != 0);
//        }
//
//        @RequiresApi(api = Build.VERSION_CODES.P)
//        @Override
//        public List<Rect> getNotchSize(Window window) {
//            ArrayList<Rect> result = new ArrayList<>();
//            View decorView = window.getDecorView();
//            WindowInsets windowInsets = decorView.getRootWindowInsets();
//            if (windowInsets == null){
//                return result;
//            }
//            DisplayCutout dct = windowInsets.getDisplayCutout();
//            if (dct != null) {
//                result.addAll(dct.getBoundingRects());
//            }
//            return result;
//        }
//
//        @RequiresApi(api = Build.VERSION_CODES.P)
//        @Override
//        public void setWindowLayoutAroundNotch(Window window) {
//            WindowManager.LayoutParams attributes = window.getAttributes();
//            attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
//            window.setAttributes(attributes);
//        }
//
//        @RequiresApi(api = Build.VERSION_CODES.P)
//        @Override
//        public void setWindowLayoutBlockNotch(Window window) {
//            WindowManager.LayoutParams attributes = window.getAttributes();
//            attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
//            window.setAttributes(attributes);
//        }
//    }

    /**
     * 华为刘海屏适配
     */
    private static class HwNotchScreenSupport implements INotchScreen {

        private static int HW_FLAG_NOTCH_SUPPORT = 0x00010000;
        private Class hwNotchSizeUtil;
        private Field hwLayoutParamsFlags;

        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public boolean hasNotchInScreen(Window window) {
            try {
                if (hwNotchSizeUtil == null) {
                    ClassLoader cl = window.getContext().getClassLoader();
                    hwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");
                }
                Method get = hwNotchSizeUtil.getMethod("hasNotchInScreen");
                return (boolean) get.invoke(hwNotchSizeUtil);
            } catch (Exception ignored) {
                return false;
            }
        }

        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public List<Rect> getNotchSize(Window window) {
            List<Rect> result = new ArrayList<Rect>();
            Rect rect = new Rect();
            try {
                Context context = window.getContext();
                if (hwNotchSizeUtil == null) {
                    ClassLoader cl = context.getClassLoader();
                    hwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");
                }
                Method get = hwNotchSizeUtil.getMethod("getNotchSize");
                int[] ret = (int[]) get.invoke(hwNotchSizeUtil);
                rect.left = (context.getResources().getDisplayMetrics().widthPixels - ret[0]) / 2;
                rect.bottom = ret[1];
                rect.right = rect.left + ret[0];
                rect.top = 0;
                result.add(rect);
                return result;
            } catch (Exception ignored) {
                return result;
            }
        }

        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public void setWindowLayoutAroundNotch(Window window) {
            ViewGroup.LayoutParams layoutParams = window.getAttributes();
            try {
                if (hwLayoutParamsFlags == null) {
                    hwLayoutParamsFlags = layoutParams.getClass().getDeclaredField("hwFlags");
                    hwLayoutParamsFlags.setAccessible(true);
                }
                int old = (int) hwLayoutParamsFlags.get(layoutParams);
                hwLayoutParamsFlags.set(layoutParams, old | HW_FLAG_NOTCH_SUPPORT);
            } catch (Exception ignored) {
            }
        }

        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public void setWindowLayoutBlockNotch(Window window) {
            ViewGroup.LayoutParams layoutParams = window.getAttributes();
            try {
                if (hwLayoutParamsFlags == null) {
                    hwLayoutParamsFlags = layoutParams.getClass().getDeclaredField("hwFlags");
                    hwLayoutParamsFlags.setAccessible(true);
                }
                int old = (int) hwLayoutParamsFlags.get(layoutParams);
                hwLayoutParamsFlags.set(layoutParams, old & ~HW_FLAG_NOTCH_SUPPORT);
            } catch (Exception ignored) {
            }
        }
    }

    /**
     * 小米刘海屏适配
     */
    private static class MiNotchScreenSupport implements INotchScreen {

        //绘制到刘海区域
        private static final int FLAG_NOTCH_IMMERSIVE = 0x00000100;
        //竖屏绘制到刘海区域
        private static final int FLAG_NOTCH_PORTRAIT = 0x00000200;
        //横屏绘制刘海区域
        private static final int FLAG_NOTCH_LANDSCAPE = 0x00000400;

        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public boolean hasNotchInScreen(Window window) {
            try {
                return SystemProperties.get("ro.miui.notch", null).equals("1");
            } catch (Exception e) {
                return false;
            }
        }

        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public List<Rect> getNotchSize(Window window) {
            ArrayList<Rect> result = new ArrayList<>();
            Context context = window.getContext();
            Rect rect = new Rect();
            rect.top = 0;
            rect.left = 0;
            rect.bottom = getStatusBarHeight(context);
            rect.right = context.getResources().getDisplayMetrics().widthPixels;
            result.add(rect);
            return result;
        }

        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public void setWindowLayoutAroundNotch(Window window) {
            int flag = FLAG_NOTCH_IMMERSIVE | FLAG_NOTCH_PORTRAIT | FLAG_NOTCH_LANDSCAPE;
            try {
                Method method = Window.class.getMethod("addExtraFlags", int.class);
                method.invoke(window, flag);
            } catch (Exception ignored) {
            }
        }

        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public void setWindowLayoutBlockNotch(Window window) {
            int flag = FLAG_NOTCH_IMMERSIVE | FLAG_NOTCH_PORTRAIT | FLAG_NOTCH_LANDSCAPE;
            try {
                Method method = Window.class.getMethod("clearExtraFlags", int.class);
                method.invoke(window, flag);
            } catch (Exception ignored) {
            }
        }
    }


    /**
     * Oppo刘海屏适配
     */
    private static class OppoNotchScreenSupport implements INotchScreen {

        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public boolean hasNotchInScreen(Window window) {
            try {
                return window.getContext().getPackageManager()
                        .hasSystemFeature("com.oppo.feature.screen.heteromorphism");
            } catch (Exception ignored) {
                return false;
            }
        }

        //目前Oppo刘海屏机型尺寸规格都是统一的,显示屏宽度为1080px，高度为2280px,刘海区域宽度为324px, 高度为80px
        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public List<Rect> getNotchSize(Window window) {
            ArrayList<Rect> result = new ArrayList<>();
            Context context = window.getContext();
            Rect rect = new Rect();
            DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
            int notchWidth = 324;
            int notchHeight = 80;
            rect.left = (displayMetrics.widthPixels - notchWidth) / 2;
            rect.right = rect.left + notchWidth;
            rect.top = 0;
            rect.bottom = notchHeight;
            result.add(rect);
            return result;
        }

        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public void setWindowLayoutAroundNotch(Window window) {
            window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
            window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            int systemUiVisibility = window.getDecorView().getSystemUiVisibility();
            systemUiVisibility = systemUiVisibility | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
            systemUiVisibility = systemUiVisibility | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
            window.getDecorView().setSystemUiVisibility(systemUiVisibility);
        }

        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public void setWindowLayoutBlockNotch(Window window) {
            window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
            window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            int systemUiVisibility = window.getDecorView().getSystemUiVisibility();
            systemUiVisibility = systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
            systemUiVisibility = systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
            window.getDecorView().setSystemUiVisibility(systemUiVisibility);
        }
    }


    /**
     * Vivo刘海屏适配
     */
    private static class VivoNotchScreenSupport implements INotchScreen {

        private Class<?> vivoFtFeature;
        //表示是否有凹槽
        private static final int VIVO_HAS_NOTCH_DISPLAY = 0x00000020;

        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public boolean hasNotchInScreen(Window window) {
            try {
                if (vivoFtFeature == null) {
                    ClassLoader cl = window.getContext().getClassLoader();
                    vivoFtFeature = cl.loadClass("android.util.FtFeature");
                }
                Method get = vivoFtFeature.getMethod("isFeatureSupport", int.class);
                return (boolean) get.invoke(vivoFtFeature, VIVO_HAS_NOTCH_DISPLAY);
            } catch (Exception ignored) {
                return false;
            }
        }

        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public List<Rect> getNotchSize(Window window) {
            ArrayList<Rect> result = new ArrayList<>();
            Context context = window.getContext();
            Rect rect = new Rect();
            DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
            int notchWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100f, displayMetrics);
            int notchHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 27f, displayMetrics);
            rect.left = (displayMetrics.widthPixels - notchWidth) / 2;
            rect.right = rect.left + notchWidth;
            rect.top = 0;
            rect.bottom = notchHeight;
            result.add(rect);
            return result;
        }

        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public void setWindowLayoutAroundNotch(Window window) {
            window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
            window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            int systemUiVisibility = window.getDecorView().getSystemUiVisibility();
            systemUiVisibility = systemUiVisibility | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
            systemUiVisibility = systemUiVisibility | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
            window.getDecorView().setSystemUiVisibility(systemUiVisibility);
        }

        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public void setWindowLayoutBlockNotch(Window window) {
            window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
            window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            int systemUiVisibility = window.getDecorView().getSystemUiVisibility();
            systemUiVisibility = systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
            systemUiVisibility = systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
            window.getDecorView().setSystemUiVisibility(systemUiVisibility);
        }
    }

    private static int getStatusBarHeight(Context context) {
        int result = 0;
        int resId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resId > 0) {
            result = context.getResources().getDimensionPixelSize(resId);
        }
        return result;
    }

}
